package com.yunzhou.interceptor;

import com.yunzhou.constants.RedisConstants;
import com.yunzhou.mapper.UserMapper;
import com.yunzhou.properties.JwtProperties;
import com.yunzhou.utils.JwtUtil;
import com.yunzhou.utils.ThreadLocalUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class RefreshTokenInterceptor implements HandlerInterceptor {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private JwtProperties jwtProperties;
    @Resource
    private UserMapper userMapper;

    /**
     * 拦截一切用户请求！！！
     * 对于已登录的用户刷新Redis中的token
     * 对于未登录的用户请求，不需要刷新Redis中的token，直接放行交给第二个登录拦截器去做未授权拦截
     *      情况1、用户没有携带Token，则直接放行
     *      情况2、用户携带Token，并且Token没有过期，则刷新Token过期时间，并将Token中的UserId放到ThreadLocal中，放行
     *      情况3、用户携带Token，但是Token过期了，则直接放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        try{
            System.out.println("request.getRequestURL()：" + request.getRequestURL());
            // System.out.println("request.getHeaderNames()：" + request.getHeaderNames());
//            Enumeration<String> headerNames = request.getHeaderNames();
//            while (headerNames.hasMoreElements()) {
//                String name = headerNames.nextElement();
//                System.out.println("request.getHeaderNames()：" + name);
//            }

            // 令牌验证
            // 1、从请求头中获取Token令牌
            String token = request.getHeader(jwtProperties.getTokenName());
            System.out.println("请求头中获取的token为：" + token);
            if(token == null) {
                // Token不存在，说明当前用户并未登录，直接放行，无需刷新Token
                return true;
            }

            // 2、用户确实携带Token，则解析token，将userId放到ThreadLocal中，并刷新Token
            // 3、用户确实携带Token，但是Token已过期，则直接放行
            // 从redis中获取相同的token
            String tokenKey = RedisConstants.LOGIN_TOKEN_KEY + token;
            Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);
            if (userMap.isEmpty()) {
                System.out.println("token已过期");
                // 没有通过token找到Redis中数据，说明Token已过期
                return true;
            }

            String redisToken = stringRedisTemplate.opsForHash().get(tokenKey, "token").toString();
            System.out.println("从Redis中获取的token为：" + redisToken);
            if (redisToken == null){ // token已失效，直接放行
                // throw new RuntimeException("token已失效，请重新登录");
                return true;
            }

            // 走到这里说明用户携带了Token，并且Token未失效
            // 获取Token中存储的用户信息
            // Map<String, Object> claims = JwtUtil.parseToken(jwtProperties.getSecretKey(), redisToken);
            // Long userId = Long.valueOf(claims.get("userId").toString());
            Long userId = Long.parseLong(stringRedisTemplate.opsForHash().get(tokenKey, "userId").toString());
            // Long userId = (Long) stringRedisTemplate.opsForHash().get(tokenKey, "userId");
            System.out.println("token中存储的UserId为: "+userId);

            // 查看token中的用户是否存在
            if(userMapper.selectByUserId(userId) == null) {
                // throw new RuntimeException("token已失效，请重新登录");
                return true; // 假的Token，直接放行
            }

            // 把用户数据存储到ThreadLocal中（通过我们自己写的ThreadLocal工具类）
            ThreadLocalUtil.setCurrentUserId(userId);

            // 只要用户在操作发送请求，就刷新token有效期，重回24h
            stringRedisTemplate.expire(tokenKey, jwtProperties.getTtl() / 1000, TimeUnit.SECONDS);

            // 3、在preHandle方法中，return true即放行操作
            return true;
        }catch (Exception e){
            System.out.println("拦截器1出错");
            e.printStackTrace();
            // 4、出错时返回的状态码为401
            response.setStatus(401);
            return false;  // 不放行
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清除ThreadLocal，防止内存泄露
        ThreadLocalUtil.remove();
    }

}
